<?php
/**
*	@author SignpostMarv Martin
*	@package UHU
*/


/**
*	If the UHU installation root wasn't defined before uhu.php was brought into scope, an Exception is thrown.
*/
if(defined('UHU_ROOT') === false)
{
	throw new Exception('UHU_ROOT not defined.');
}
/**
*	If the UHU library location wasn't defined before uhu.php was brought into scope, an Exception is thrown.
*/
if(defined('UHU_LIBS') === false)
{
	throw new Exception('UHU_LIBS not defined.');
}
/**
*	If the UHU installation root wasn't defined before uhu.php was brought into scope, it is set to a default of UHU_ROOT . "/apps"
*/
if(defined('UHU_APPS') === false)
{
	define('UHU_APPS',UHU_ROOT . '/apps');
}

require_once(UHU_ROOT . '/uhu/PHP-config.php');
require_once(UHU_ROOT . '/uhu/Exception.php');
require_once(UHU_ROOT . '/uhu/config.php');
require_once(UHU_ROOT . '/uhu/loadables.php');

/**
*	@package UHU
**/
class uhu
{
/**
*	@var array
*	@see uhu_component
*/
	protected static $components = array();
/**
*	@var array
*	@see uhu_application
*/
	protected static $apps       = array();
/**
*	@var array
*	@see uhu_lib
*/
	protected static $libs       = array();
/**
*	@var array
*	@uses uhu_application
*	@uses uhu_lib
*	@uses uhu_component
*/
	protected static $supported_loadables = array(
		uhu_application::short_type=>uhu_application::full_type,
		uhu_lib::short_type=>uhu_lib::full_type,
		uhu_component::short_type=>uhu_component::full_type,
	);
/**
*	@var string|null
*/
	protected $path_to_config = null;
/**
*	The path UHU components are installed in
*	@see uhu_component
*	@uses UHU_ROOT
*/
	const path_to_components = UHU_ROOT;
/**
*	The path UHU applications are installed in
*	@see uhu_application
*	@uses UHU_APPS
*/
	const path_to_apps       = UHU_APPS;
/**
*	The path UHU libraries are installed in
*	@see uhu_lib
*	@uses UHU_LIBS
*/
	const path_to_libs       = UHU_LIBS;
/**
*	Loads the configuration for the UHU isntallation, adds the UHU library path to the PHP include path.
*	@uses uhu::$path_to_config
*	@uses uhu::$path_to_libs
*	@uses UHU_CONFIG_PATH
*	@uses UHU_ROOT
*	@see UHU_Config
*/
	protected function __construct()
	{
		if(isset($this->path_to_config) === false)
		{
			$this->path_to_config = defined('UHU_CONFIG_PATH') ? UHU_CONFIG_PATH : UHU_ROOT . '/uhu-config.php';
		}
		include_once($this->path_to_config);
		set_include_path(implode(PATH_SEPARATOR,
			array(
				self::path_to_libs,
				self::path_to_components,
				self::path_to_apps,
			)
		) . PATH_SEPARATOR . get_include_path());
	}
/**
*	Returns a reference to the array holding the specified type of uhu_loadable
*	@param string $type app, lib or component
*	@see uhu_loadable
*	@uses uhu::$supported_loadables
*	@uses uhu::$apps
*	@uses uhu::$libs
*	@uses uhu::$components
*	@return array Returns the array the loadable is meant to go into.
*/ 
	protected static function & loadable_destination($type)
	{
		if(array_key_exists($type,self::$supported_loadables) === false)
		{
			throw new uhu_loadable_exception('unsupported loadable type \'' . get_class($loadable) . '\' specified.',301);
		}
		switch($type)
		{
			case 'app':
				$dest = & self::$apps;
			break;
			case 'lib':
				$dest = & self::$libs;
			break;
			case 'component':
				$dest = & self::$components;
			break;
		}
		return $dest;
	}
/**
*	Registers a loadable of the specified type to be available for later loading.
*	@param uhu_loadable $loadable The loadable to add
*	@param string $type app, lib or component
*	@uses uhu::loadable_destination
*	@uses uhu::$supported_loadables
*	@uses uhu_loadable::label()
*/
	protected static function add_loadable(uhu_loadable $loadable,$type)
	{
		$dest = & self::loadable_destination($type);
		$fulltype = self::$supported_loadables[$type];
		if(isset($dest[$loadable->label()]))
		{
			throw new uhu_loadable_exception(sprintf('%2$s \'%1$s\' already registered',$loadable->label(),$fulltype),300);
		}
		else
		{
			$dest[$loadable->label()] = $loadable;
		}
	}
/**
*	Attempts to load loadables of the specified type
*	@param array $labels An array of loadable labels to attempt to load.
*	@param string $type app, lib or component
*	@see uhu_loadable
*	@uses uhu::loadable_destination()
*	@uses uhu::$supported_loadables
*	@uses uhu_loadable_exception
*	@uses uhu_loadable::load()
*/
	protected static function load_loadables(array $labels,$type)
	{
		$dest = & self::loadable_destination($type);
		$fulltype = self::$supported_loadables[$type];
		if(empty($dest))
		{
			new self;
			if(empty($dest))
			{
				$plural = strtolower($fulltype) . 's';
				throw new uhu_loadable_exception(sprintf('No %s registered.',$plural),203);
			}
		}
		foreach($labels as $loadable_label)
		{
			if(in_array($loadable_label,array_keys($dest)) === false)
			{
				throw new uhu_loadable_exception(sprintf('%2$s \'%1$s\' not registered',$loadable_label,$fulltype),202);
			}
			else
			{
				$dest[$loadable_label]->load();
			}
		}
	}
/**
*	Returns an array of the loadable labels of the given type
*	@param string type $type app, lib or component
*	@uses uhu::loadable_destination()
*	@return array
*/
	protected static function list_loadables($type)
	{
		return array_keys(self::loadable_destination($type));
	}
/**
*	Returns an array of the labels for the loaded apps
*	@uses uhu::list_loadables()
*	@return array
*/
	public static function apps()
	{
		return self::list_loadables('app');
	}
/**
*	Returns an array of the labels for the loaded libraries
*	@uses uhu::list_loadables()
*	@return array
*/
	public static function libs()
	{
		return self::list_loadables('lib');
	}
/**
*	Returns an array of the labels for the loaded components
*	@uses uhu::list_loadables()
*	@return array
*/
	public static function components()
	{
		return self::list_loadables('component');
	}
/**
*	Atempt to load the specified UHU applications
*	@param string $application,...
*	@uses uhu::load_loadables()
*/
	public static function load_apps()
	{
		$args = func_get_args();
		self::load_loadables($args,'app');
	}
/**
*	Atempt to load the specified UHU components
*	@param string $component,...
*	@uses uhu::load_loadables()
*/
	public static function load_components()
	{
		$args = func_get_args();
		self::load_loadables($args,'component');
	}
/**
*	Atempt to load the specified UHU libraries
*	@param string $library,...
*	@uses uhu::load_loadables()
*/
	public static function load_libs()
	{
		$args = func_get_args();
		self::load_loadables($args,'lib');
	}
/**
*	Attempt to register the specified component for later loading
*	@param uhu_component $component
*	@see uhu_component::__construct()
*	@uses uhu::add_loadable()
*/
	public static function add_component(uhu_component $component)
	{
		self::add_loadable($component,'component');
	}
/**
*	Attempt to register the specified application for later loading
*	@param uhu_application $app
*	@see uhu_application::__construct()
*	@uses uhu::add_loadable()
*/
	public static function add_app(uhu_application $app)
	{
		self::add_loadable($app,'app');
	}
/**
*	Attempt to register the specified component for later loading
*	@param uhu_lib $lib
*	@see uhu_lib::__construct()
*	@uses uhu::add_loadable()
*/
	public static function add_lib(uhu_lib $lib)
	{
		self::add_loadable($lib,'lib');
	}
/**
*	Acts as a wrapper to {@link @link http://uk.php.net/manual/en/ref.curl.php PHP cURL}
*	@param string $url the resource to fetch
*	@param string $method the  method to use with the resource
*	@param array|null $options  {@link http://uk.php.net/manual/en/function.curl-setopt-array.php}
*	@param array|null $vars To be used with a POST operation
*	@param array|null $add_info {@link http://uk.php.net/manual/en/function.curl-getinfo.php}
*	@return stdClass an object containing the data/error information regarding the transfer.
*/
	public static function curl($url,$method='GET',$options=NULL,$vars=NULL,$add_info=NULL)
	{
		$options = empty($options) ? array() : $options;
		if(is_array($options) === true)
		{
			if(array_key_exists(CURLOPT_URL,$options) === false)
			{
				$options[CURLOPT_URL] = $url;
			}
			if(array_key_exists(CURLOPT_HEADER,$options) === false)
			{
				$options[CURLOPT_HEADER] = false;
			}
			if(array_key_exists(CURLOPT_FOLLOWLOCATION,$options) === false)
			{
				$options[CURLOPT_FOLLOWLOCATION] = 1;
			}
			if(array_key_exists(CURLOPT_RETURNTRANSFER,$options) === false)
			{
				$options[CURLOPT_RETURNTRANSFER] = 1;
			}
		}
		if(is_array($options) && array_key_exists(CURLOPT_TIMEVALUE,$options) === true)
		{
			$options[CURLOPT_FILETIME] = true;
			if(isset($options[CURLOPT_TIMECONDITION]) === false)
			{
				$options[CURLOPT_TIMECONDITION] = CURL_TIMECOND_IFMODSINCE;
			}
		}
		if ($method === 'POST')
		{
			$options[CURLOPT_POST] = 1;
			if(is_array($vars) === true)
			{
				$options[CURLOPT_POSTFIELDS] = $vars;
			}
		}
		$ch = curl_init();
		if(is_array($options) === true)
		{
			curl_setopt_array($ch,$options);
		}
		$obj = new stdClass;
		$obj->data = curl_exec($ch);
		if(is_array($options) === true && array_key_exists(CURLOPT_TIMEVALUE,$options) === true)
		{
			$obj->modtime = curl_getinfo($ch,CURLINFO_FILETIME);
		}
		if(is_array($add_info) === true)
		{
			$obj->info = array();
			foreach($add_info as $v)
			{
				$obj->info[$v] = curl_getinfo($ch,$v);
			}
		}
		if ($obj->data === false)
		{
			$obj->error = curl_error($ch);
		}
		curl_close($ch);
		return $obj;
	}
/**
*	Used to decide if a given string is a UUID
*	@param string $string
*	@staticvar array $uuids An array storing positive UUID checks
*	@staticvar array $not An array storing negative UUID checks
*	@return bool TRUE if $tring is a UUID, FALSE otherwise
*/
	final public static function is_uuid($string)
	{
#	Research with PEAR's Benchmark package has shown that caching the result doesn't given the desired effect
		if(is_string($string) === false)
		{
			return false;
		}
		else
		{
#	Crediting Tempus Heart for pointing out rather major logical flaw with regex pattern going from a-z.
			return (bool)preg_match('/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/S',$string);
		}
	}
/**
*	Correctly performs an exclusive or on two Base 64 strings. A PHP implementation of the LSL function {@link http://wiki.secondlife.com/wiki/llXorBase64StringsCorrect llXorBase64StringsCorrect}
*	@param string $s1 Base64 encoded string
*	@param string $s2 Base64 encoded string
*	@return string
*/
	final public static function llXorBase64StringsCorrect($s1,$s2)
	{
		if(strlen(base64_decode($s1)) > strlen(base64_decode($s2)))
		{
			$s2 = base64_encode(str_pad(base64_decode($s2),strlen(base64_decode($s1)),base64_decode($s2),STR_PAD_RIGHT));
		}
		return base64_encode(base64_decode($s1) ^ base64_decode($s2));
	}
/**
*	Returns a string of 32 hex characters that is the MD5 checksum of src with a salt of ":"+nonce. A PHP implementation of the LSL function {@link http://wiki.secondlife.com/wiki/LlMD5String llMD5String}
*	@uses uhu::llMD5String()
*	@param string $string The string to hash
*	@param integer $nonce The integer to combine with the string
*	@return string returns an MD5 string.
*/
	final public static function llMD5String($string,$nonce=0)
	{
		$nonce = (int)$nonce;
		static $hashes = array();
		if(isset($hashes[$string],$hashes[$string][$nonce]) === false)
		{
			$hashes[$string][$nonce] = md5("$string:$nonce");
		}
		return $hashes[$string][$nonce];		
	}
/**
*	If not set, sets the specified variable to the given value
*/
	final public static function set_default(& $variable,$value)
	{
		$variable = isset($variable) ? $variable : $value;
	}
/**
*	Default Exception handler for UHU
*	@uses headerBySAPI()
*/
	public static function Exception_handler(Exception $e)
	{
		if(is_string(uhu_request_config::getInstance()->If_Modified_Since()) && ($_SERVER['REQUEST_TIME'] - strtotime(uhu_request_config::getInstance()->If_Modified_Since())) < 30)
		{
			headerBySAPI('HTTP/1.1 304 Not Modified');
			exit;
		}
		else
		{
			try
			{
				$ob_status = ob_get_status();
				if(empty($ob_status) === false)
				{
					ob_clean();
				}
			}
			catch(Exception $e)
			{}
			header('Content-Type:text/plain');
			headerBySAPI('HTTP/1.1 500 Internal Server Error');
			header('Expires:' . gmdate('r',time() + 30));
			header('Last-Modifie:' . gmdate('r',$_SERVER['REQUEST_TIME']));
			$error = 'There seems to be a problem in showing you the page you requested.' . "\n" .
			'If the problem persists, please contact ' . uhu_install_config::get('uhu::site administrator') . ', citing the following:' . "\n\n" .
			get_class($e) . ' error code ' . (int)$e->getCode();
			if($e instanceof uhu_loadable_exception)
			{
				$error .= "\n" .'error message: ';
				switch($e->getCode())
				{
					case 100:
						$error .= 'an uhu loadable required for this application is missing.';
					break;
					default:
						$error .= 'there was a problem loading a required part of this application.';
					break;
				}
			}
			if(defined('UHU_DEBUG_MODE'))
			{
				$error .= "\n" . (string)$e;
			}
			header('Content-Length:' . strlen($error));
			die($error);
		}
	}
}

require_once(UHU_ROOT . '/uhu/headerBySAPI.php');
require_once(UHU_ROOT . '/uhu/request_config.php');
require_once(UHU_ROOT . '/uhu/HTTP_ACCEPT.php');
require_once(UHU_ROOT . '/uhu/wrapper-funcs.php');
require_once(UHU_ROOT . '/uhu/simple-401.php');
?>